#include <tchar.h>
#include "GoogleService.hpp"
#include <stdio.h>
#include <assert.h>

CGoogleService::CGoogleService(LPCTSTR pSource, bool bSSL, LPCTSTR pService)
{
	_tcsncpy(m_szSource, pSource, sizeof(m_szSource));
	_tcsncpy(m_szService, pService, sizeof(m_szService));
	m_szAuth[0] = _T('\0');
	m_hSession = InternetOpen(_T("SMITH"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
	m_hConnect = NULL;
	m_bSSL = bSSL;
	if(m_hSession) {
		if(m_bSSL) {
			m_hConnect = InternetConnect(m_hSession, _T("www.google.com"), INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);
		} else {
			m_hConnect = InternetConnect(m_hSession, _T("www.google.com"), INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);
		}
	}
}

CGoogleService::~CGoogleService()
{
	if(m_hConnect) InternetCloseHandle(m_hConnect);
	if(m_hSession) InternetCloseHandle(m_hSession);
}

bool CGoogleService::Auth(LPCTSTR pEmail, LPCTSTR pPassword)
{
	HINTERNET hRequest;

	TCHAR szURL[200];
	LPTSTR pFormat = _T("/accounts/ClientLogin?accountType=HOSTED_OR_GOOGLE&Email=%s&Passwd=%s&&service=%s&source=%s");
	_sntprintf(szURL, sizeof(szURL), pFormat, pEmail, pPassword, m_szService, m_szSource);
	static LPCTSTR pAcceptType[] = { _T("Accept: */*"), NULL };
	hRequest = HttpOpenRequest(m_hConnect, _T("POST"), szURL, NULL, NULL, pAcceptType, INTERNET_FLAG_KEEP_CONNECTION|INTERNET_FLAG_RELOAD|INTERNET_FLAG_SECURE, 0);
	if(!hRequest) {
		return false;
	}
	const TCHAR szHeaders[] = _T("Content-Type: application/x-www-form-urlencoded;charset=utf-8\r\n");
	if(!HttpSendRequest(hRequest, szHeaders, lstrlen(szHeaders), NULL, 0)) {
		GetLastError();
		InternetCloseHandle(hRequest);
		return false;
	}

	DWORD  dwSize = 0;
	CHAR   szData[2000];
	DWORD  dwDownloaded;
	for(;;) {
		if(!InternetReadFile(hRequest, szData+dwSize, sizeof(szData)-dwSize, &dwDownloaded)) {
			InternetCloseHandle(hRequest);
			return false;
		}
		if(dwDownloaded==0) break;
		dwSize += dwDownloaded;
		if(dwSize>=sizeof(szData)-1) {
			InternetCloseHandle(hRequest);
			return false;
		}
	}
	InternetCloseHandle(hRequest);
	szData[dwSize]='\0';

	char* pSID = strstr(szData, "SID");
	char* pLSID = strstr(szData, "LSID");
	char* pAuth = strstr(szData, "Auth");
	if(!pSID || !pLSID || !pAuth) return false;
	*strchr(pSID, '\n') = '\0';
	*strchr(pLSID, '\n') = '\0';
	*strchr(pAuth, '\n') = '\0';
	if(!Utf8ToTCHAR(pSID+4,  m_szSID,  sizeof(m_szSID))) return false;
	if(!Utf8ToTCHAR(pLSID+5, m_szLSID, sizeof(m_szLSID))) return false;
	if(!Utf8ToTCHAR(pAuth+5, m_szAuth, sizeof(m_szAuth))) return false;

	if(m_bSSL) {
		InternetSetCookie(_T("https://www.google.com/"), _T("SID"), m_szSID);
	} else {
		InternetSetCookie(_T("http://www.google.com/"), _T("SID"), m_szSID);
	}

	return true;
}

bool CGoogleService::IsLogined() {
	return m_szAuth[0]!=_T('\0');
}

bool CGoogleService::HttpGet(LPCTSTR pURL, LPSTR pData, DWORD& dwDataSize, bool bSSL)
{
	HINTERNET hRequest;

	static LPCTSTR pAcceptType[] = { _T("Accept: */*"), NULL };
	hRequest = HttpOpenRequest(m_hConnect, _T("GET"), pURL, NULL, NULL, pAcceptType, INTERNET_FLAG_KEEP_CONNECTION|INTERNET_FLAG_RELOAD|(m_bSSL?INTERNET_FLAG_SECURE:0), 0);
	if(!hRequest) return false;
	if(!HttpSendRequest(hRequest, NULL, 0, NULL, 0)) {
		InternetCloseHandle(hRequest);
		return false;
	}

	DWORD dwSize = 0;
	DWORD dwDownloaded;
	for(;;) {
		if(!InternetReadFile(hRequest, pData+dwSize, dwDataSize-dwSize, &dwDownloaded)) {
			InternetCloseHandle(hRequest);
			return false;
		}
		if(dwDownloaded==0) break;
		dwSize += dwDownloaded;
		if(dwSize>=dwDataSize-1) {
			InternetCloseHandle(hRequest);
			return false;
		}
	}
	pData[dwSize]='\0';
	dwDataSize = dwSize;

	InternetCloseHandle(hRequest);
	return true;
}

bool CGoogleService::GetImage(LPCTSTR pURL, LPCTSTR pLocalFile)
{
	HINTERNET hURL = InternetOpenUrl(m_hSession, pURL, NULL, 0, INTERNET_FLAG_EXISTING_CONNECT|INTERNET_FLAG_RAW_DATA, 0);
	if(!hURL) return false;

	HANDLE hFile = CreateFile(pLocalFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL);
	if(!hFile) {
		InternetCloseHandle(hURL);
		return false;
	}

	char pData[1*1024];
	DWORD dwSize = 0;
	DWORD dwDownloaded;
	for(;;) {
		if(!InternetReadFile(hURL, pData, sizeof(pData), &dwDownloaded)) {
			InternetCloseHandle(hURL);
			CloseHandle(hFile);
			return false;
		}
		if(dwDownloaded==0) break;
		DWORD dwWriteSize = 0;
		for(; dwWriteSize<dwDownloaded; ) {
			DWORD dwBytes;
			if(!WriteFile(hFile, pData+dwWriteSize, dwDownloaded-dwWriteSize, &dwBytes, NULL)) {
				InternetCloseHandle(hURL);
				CloseHandle(hFile);
				return false;
			}
			dwWriteSize += dwBytes;
		}
	}

	InternetCloseHandle(hURL);
	CloseHandle(hFile);
	return true;
}

bool Utf8ToTCHAR(LPCSTR pUtf8, LPTSTR pStr, DWORD nSize)
{
	if(*pUtf8=='\0') {
		*pStr = _T('\0');
		return true;
	}

#ifdef UNICODE
	int ret;
	ret = MultiByteToWideChar(CP_UTF8, 0, pUtf8, strlen(pUtf8), pStr, nSize);
	if(ret==0) return false;
	pStr[ret] = L'\0';
#else

	wchar_t* txt = (wchar_t*)malloc(strlen(pUtf8)*2+10);
	int ret;

	ret = MultiByteToWideChar(CP_UTF8, 0, pUtf8, strlen(pUtf8), txt, strlen(pUtf8)+10);
	if(ret==0) { free(txt); return false; }
	txt[ret] = L'\0';
	ret = WideCharToMultiByte(CP_ACP, 0, txt, wcslen(txt), pStr, (int)nSize, NULL, NULL);
	free(txt);
	if(ret==0) return false;
	pStr[ret] = _T('\0');
#endif
	return true;
}

bool TCHARToUtf8(LPCTSTR pStr, LPSTR pUtf8, DWORD nSize)
{
	if(*pStr=='\0') {
		*pUtf8 = _T('\0');
		return true;
	}

#ifdef UNICODE
	int ret;
	ret = WideCharToMultiByte(CP_UTF8, 0, pStr, wcslen(pStr), pUtf8, (int)nSize, NULL, NULL);
	if(ret==0) return false;
	pUtf8[ret] = _T('\0');
	return true;
#else
	wchar_t* txt = (wchar_t*)malloc(strlen(pStr)+10);
	int ret;
	ret = MultiByteToWideChar(CP_ACP, 0, pStr, strlen(pUtf8), txt, strlen(pStr)+10);
	if(ret==0) { free(txt); return false; }
	txt[ret] = L'\0';
	ret = WideCharToMultiByte(CP_UTF8, 0, txt, wcslen(txt), pUtf8, (int)nSize, NULL, NULL);
	free(txt);
	if(ret==0) return false;
	pUtf8[ret] = _T('\0');
	return true;
#endif
}

static bool UrlEncodeA(LPCSTR pFrom, LPSTR pTo, DWORD nSize)
{
	size_t i, w = 0, len = strlen(pFrom);

	for(i=0; i<len; i++) {
        if(	(48 <= pFrom[i] && pFrom[i] <= 57) ||//0-9
			(65 <= pFrom[i] && pFrom[i] <= 90) ||//abc...xyz
			(97 <= pFrom[i] && pFrom[i] <= 122) || //ABC...XYZ
			(pFrom[i]=='~' || pFrom[i]=='!' || pFrom[i]=='*' || pFrom[i]=='(' || pFrom[i]==')' || pFrom[i]=='\\'))
		{
			pTo[w++] = pFrom[i];
		} else {
		    char dig1 = (pFrom[i]&0xF0)>>4;
			char dig2 = (pFrom[i]&0x0F);
			if ( 0<= dig1 && dig1<= 9) dig1+=48;    //0,48inascii
			if (10<= dig1 && dig1<=15) dig1+=97-10; //a,97inascii
			if ( 0<= dig2 && dig2<= 9) dig2+=48;
			if (10<= dig2 && dig2<=15) dig2+=97-10;
			pTo[w+0] = '%';
			pTo[w+1] = dig1;
			pTo[w+2] = dig2;
			w += 3;
		}
	}
	pTo[w] = '\0';

	return true;
}

bool UrlEncode(LPCTSTR pFrom, LPTSTR pTo, DWORD nSize)
{
#ifdef _UNICODE
	char szFrom[400];
	char szTo[400];
	if(!TCHARToUtf8(pFrom, szFrom, sizeof(szFrom))) return false;
	if(!UrlEncodeA(szFrom, szTo, sizeof(szTo))) return false;
	if(!Utf8ToTCHAR(szTo, pTo, nSize)) return false;
	return true;
#else
	return UrlEncodeA(pFrom, pTo, nSize);
#endif
}
